using System;
using XTCast.Mp3;
using System.Web;
using System.Data;
using System.IO;
using System.Text;

namespace XTCast.Radio.RioReceiver {
	
	public enum RequestType{Content,Tags,List,Query,Results};

	public class Base:System.Web.IHttpHandler {
		//internal Station fStation;
		//PlaylistFilter fPlaylistFilter;
		//internal IMediaLibrary fLibrary;

		RequestType reqType ;

		protected internal Base(RequestType reqType) {
			this.reqType=reqType;
		}
		
		public virtual int getFileId(System.String uri_stem) {
			int lastSlash = uri_stem.LastIndexOf((System.Char) '/');
			if (lastSlash > 1) {
				int id = System.Convert.ToInt32(uri_stem.Substring(lastSlash + 1, (uri_stem.Length) - (lastSlash + 1)), 16);
				return id;
			}
			return 0;
		}

		internal const int ID_ALL = 0x100;
		internal const int ID_PLAYLIST = 0x1000;
		internal const int ID_TUNE = 0x3000;
		
		internal const int SEARCH_ARTIST = 1;
		internal const int SEARCH_ALBUM = 2;
		internal const int SEARCH_GENRE = 4;
		internal const int SEARCH_TITLE = 8;


		string MakeWhere(string searchStr, int fields) {
			if ((Object) searchStr == null || searchStr.Equals(""))
				return "";

			string searchStrQuoted = searchStr.Replace("'","''");
			string where="";
			
			if ((fields & SEARCH_ARTIST) != 0) {
				where += String.Format("{0}Artist LIKE '%{1}%'",(where=="")?"":" AND ",searchStrQuoted);
			}
			if ((fields & SEARCH_ALBUM) != 0) {
				where += String.Format("{0}Album LIKE '%{1}%'",(where=="")?"":" AND ",searchStrQuoted);
			}
			if ((fields & SEARCH_ALBUM) != 0) {
				where += String.Format("{0}Title LIKE '%{1}%'",(where=="")?"":" AND ",searchStrQuoted);
			}
			return where;
		}
		
		public virtual DataView search(System.String searchStr, int fields, DataTable dt) {
			return new DataView(dt,MakeWhere(searchStr, fields),"Artist,Album,Title",DataViewRowState.CurrentRows);
		}

		public virtual DataView search(int fileID, DataTable dt) {
			return new DataView(dt,"FileID="+fileID,"",DataViewRowState.CurrentRows);
		}

		public void ProcessRequest(System.Web.HttpContext context) {
			try {
				Station station = (Station)context.Application.Get("Station"); 
				if (station==null) {
					context.Response.Write("Current station is not loaded");
					return;
				} else SendPage(station,context.Request,context.Response);
			} catch (Exception ex) {
				Log.Error(ex);
			}
		}


		private byte[] make_item(byte num, string value) {
			return make_item(num,value,false);
		}

		private byte[] make_item(byte num, string text, bool inclIfNull) {
			if ((System.Object) text != null) {				
				byte len = (byte) text.Length;
				Encoding encoding =	Encoding.GetEncoding("ISO-8859-1");
				byte[] raw=new byte[encoding.GetByteCount(text)+2];
				raw[0]=num;
				raw[1]=len;
				encoding.GetBytes(text,0,text.Length,raw,2);
				return raw;
			}
			else if(inclIfNull) {
				return new byte[] { 0x0 };
			}
			return new byte[0];
		}
		
		public bool SendPage(Station station, HttpRequest request, HttpResponse response) {
			IMediaLibrary mlib = station.MusicLibrary;
			switch (reqType) {
				case RequestType.Content: {
					int fid = getFileId(request.Url.AbsolutePath);
					if (fid == ID_ALL) {
						response.ContentType="text/plain";
						response.Write(System.Convert.ToString(ID_PLAYLIST,16)+"=P(All)\n");
					} else if (fid == ID_PLAYLIST) {
						list(mlib.DataTable.DefaultView, response, request.QueryString["_extended"]);
					} else {
						MP3File mp3 = mlib.GetFileByID(fid-ID_TUNE);
						if ((System.Object) request.Headers["Range"] != null) {
							System.String[] range = request.Headers["Range"].Split('=');
							if (!range[0].ToUpper().Equals("bytes".ToUpper())) {
								Log.Info(this, "Playing: " + mp3);
								response.ContentType="audio/mpeg";
								response.WriteFile(mp3.FullName);
							}
							else {
								range = range[1].Split('-');
								if (range[0].Equals("0"))
									Log.Info(this, "Playing: " + mp3);
								response.ContentType="audio/mpeg";
								long from=System.Int64.Parse(range[0]);
								long to=System.Int64.Parse(range[1]);
								if (to>=mp3.LengthBytes) to=mp3.LengthBytes-1;
								response.WriteFile(mp3.FullName, from, to-from+1 );
							}
						}
						else {
							Log.Info(this, "Playing: " + mp3);
							response.ContentType="audio/mpeg";
							response.WriteFile(mp3.FullName);
						}	
					}
					break;
				}
				case RequestType.Tags: {
					int fid = getFileId(request.Url.AbsolutePath);
					response.ContentType="text/plain";
					tags(fid,mlib,response);
					break;
				}
				
				case RequestType.List: {
					Log.Info(this, "List: " + request.Url);
					response.ContentType="text/plain";
					int ntrack = getFileId(request.Url.AbsolutePath);
					if (ntrack == ID_ALL) {
						list(mlib.DataTable.DefaultView, response, request.QueryString["_extended"]);
					}
					else {
						DataView tracks = search(ntrack,mlib.DataTable);
						list(tracks, response, request.QueryString["_extended"]);
					}
					break;
				}
				
				case RequestType.Query: {
					Log.Info(this, "Query: " + request.Url);
					response.ContentType="text/plain";
					query(request,response,mlib.DataTable);
					break;
				}
				
				case RequestType.Results: 
					Log.Info(this, "Results: " + request.Url);
					response.ContentType="text/plain";
					results(response, request, mlib);
					break;
			}
			return true;
		}
		
		public override System.String ToString() {
			return "RioReceiver";
		}

		private void tags(int fid, IMediaLibrary mlib,HttpResponse response) {
			if (fid == 0x100) {
				response.BinaryWrite(make_item(0, fid.ToString()));
				response.BinaryWrite(make_item(1, "All Tracks"));
				response.BinaryWrite(make_item(7, "playlist"));
				response.BinaryWrite(make_item(6, "" + (char)(0x34)));
			}
			else {
				MP3File mp3 = mlib.GetFileByID(fid-ID_TUNE);
				mp3.ReadTags();
				Log.Info(this, "Tags: " + mp3);
				response.BinaryWrite(make_item(2, mp3.Artist));
				response.BinaryWrite(make_item(10, mp3.BitRate.ToString()));
				response.BinaryWrite(make_item(12, "mp3"));
				response.BinaryWrite(make_item(5, mp3.Comment));
				response.BinaryWrite(make_item(14, mp3.Length.TotalMilliseconds.ToString()));
				response.BinaryWrite(make_item(0, fid.ToString()));
				response.BinaryWrite(make_item(9, mp3.Genre));
				response.BinaryWrite(make_item(6, mp3.LengthBytes.ToString()));
				response.BinaryWrite(make_item(13, mp3.Tags.EndingOffset.ToString()));
				response.BinaryWrite(make_item(8, mp3.FullName));
				response.BinaryWrite(make_item(3, mp3.Album));
				response.BinaryWrite(make_item(1, mp3.Title));
				response.BinaryWrite(make_item(7, "tune"));
				response.BinaryWrite(make_item(4, mp3.Year));
			}
			response.BinaryWrite(new byte[]{0xff});
		}
		
		private byte[] pack(int nfile, int filesize, int unknown) {
			byte[] bytes = new byte[12];
			int pos = 0;
			for (int i = 3; i >= 0; i--)
				bytes[pos++] = (byte) ((nfile >> (24 - (i * 8))) & 0xFF);
			for (int i = 3; i >= 0; i--)
				bytes[pos++] = (byte) ((filesize >> (24 - (i * 8))) & 0xFF);
			for (int i = 3; i >= 0; i--)
				bytes[pos++] = (byte) ((unknown >> (24 - (i * 8))) & 0xFF);
			return bytes;
		}
		
		private void  results(HttpResponse response, HttpRequest request, IMediaLibrary mlib) {
			int searchFields = 0;
			searchFields += ((System.Object) request.QueryString["artist"] != null?SEARCH_ARTIST:0);
			searchFields += ((System.Object) request.QueryString["source"] != null?SEARCH_ALBUM:0);
			searchFields += ((System.Object) request.QueryString["genre"] != null?SEARCH_GENRE:0);
			searchFields += ((System.Object) request.QueryString["title"] != null?SEARCH_TITLE:0);
			
			System.String searchStr = request.QueryString["artist"];
			if ((System.Object) searchStr == null)
				searchStr = request.QueryString["source"];
			if ((System.Object) searchStr == null)
				searchStr = request.QueryString["genre"];
			if ((System.Object) searchStr == null)
				searchStr = request.QueryString["title"];
			
			DataView dv = search(searchStr, searchFields,mlib.DataTable);
			list(dv, response, request.QueryString["_extended"]);
		}
		
		private void  list(DataView dv, HttpResponse response, string extended) {
			if ((System.Object) extended == null || extended.Equals("1")) {
				foreach (DataRowView r in dv) {
					response.Write(System.Convert.ToString((int)(r["FileID"])+ID_TUNE , 16) + "=T" + r["Title"] + "\n");
				}
			}
			else {
				foreach (DataRowView r in dv) {
					response.BinaryWrite(pack((int)(r["FileID"])+ID_TUNE, (int)((MP3File)r["MP3File"]).LengthBytes, 0));
				}
			}
		}

		public virtual System.Collections.IDictionary querylist(DataView dv, System.String tag) {
			System.Collections.IDictionary results = new System.Collections.SortedList(System.Collections.CaseInsensitiveComparer.Default);
			foreach (DataRowView r in dv) {
				try {
					System.String name = ((string)r[tag]).Trim();
					
					object val = results[name];
					if (val == null) {
						results[name] = 1;
					} else {
						int vl = (int)val;
						results[name] = vl+1;
					}
				}
				catch (System.Exception e) {
				}
			}
			return results;
		}
		
		private void  query(HttpRequest request, HttpResponse response, DataTable dt) {
			response.Write("matches=\n");
			
			int searchFields = 0;
			searchFields += ((System.Object) request.QueryString["artist"] != null?SEARCH_ARTIST:0);
			searchFields += ((System.Object) request.QueryString["source"] != null?SEARCH_ALBUM:0);
			searchFields += ((System.Object) request.QueryString["genre"] != null?SEARCH_GENRE:0);
			searchFields += ((System.Object) request.QueryString["title"] != null?SEARCH_TITLE:0);
			
			System.String searchStr = request.QueryString["artist"];
			if ((System.Object) searchStr == null)
				searchStr = request.QueryString["source"];
			if ((System.Object) searchStr == null)
				searchStr = request.QueryString["genre"];
			if ((System.Object) searchStr == null)
				searchStr = request.QueryString["title"];
			
			System.String listTag = "Artist";
			if ((System.Object) request.QueryString["source"] != null)
				listTag = "Album";
			if ((System.Object) request.QueryString["genre"] != null)
				listTag = "Genre";
			if ((System.Object) request.QueryString["title"] != null)
				listTag = "Title";
			
			DataView tracks = search(searchStr, searchFields, dt);
			System.Collections.IDictionary map = querylist(tracks, listTag);
			
			int count = 0;
			foreach (string name in map.Keys) {
				response.Write((count++) + "=" + map[name] + ",0,0:" + name + "\n");
			}
		}
	
		public bool IsReusable {
			get {
				return true;
			}
		}

	}

	public class Content:Base,System.Web.IHttpHandler {
		Content():base(RequestType.Content) {}
	}
	public class Tags:Base,System.Web.IHttpHandler {
		Tags():base(RequestType.Tags) {}
	}
	public class List:Base,System.Web.IHttpHandler {
		List():base(RequestType.List) {}
	}
	public class Query:Base,System.Web.IHttpHandler {
		Query():base(RequestType.Query) {}
	}
	public class Results:Base,System.Web.IHttpHandler {
		Results():base(RequestType.Results) {}
	}
	
}